/* Copyright (c) 2009-2010, Sean A.O. Harney <sharney3@gmail.com> */

#include "omf.h"

/* prototypes */
EXPR_P _expr_eval(EXPR_P expr, int levels_deep);
EXPR_P _expr_eval_leaf(EXPR_P expr, int levels_deep);
EXPR_P _expr_eval_binary(EXPR_P expr, int levels_deep);
EXPR_P _expr_eval_binary_op_list_cons(EXPR_P lhs_evaled,
				      EXPR_P leaf_list_rhs,
				      int levels_deep);
EXPR_P _expr_eval_assignment(EXPR_P expr);
EXPR_P _expr_eval_functioncall(EXPR_P expr);


/* recursively evaluate tree starting at root node expr */
/* returns EXPR_P to (for now: leaf' */
EXPR_P expr_eval(EXPR_P expr)
{
    return _expr_eval(expr, 0);
}

/* recursively evaluate tree starting at root node expr */
EXPR_P _expr_eval(EXPR_P expr, int levels_deep)
{
    EXPR_P result = NULL;
    char tabs[10];
    int i;

    for (i = 0; i < levels_deep && i < sizeof(tabs) - 1; i++)
	tabs[i] = '\t';
    tabs[i] = '\0';
    if (expr == NULL)
	error_println("expr_eval(expr = NULL.");	//assert expr not null

    debug2_printf("%sexpr_eval(type=%s, levels_deep=%d) ", tabs,
		  expr_type_to_str(expr->type), levels_deep);
    switch (expr->type)
      {
      case EXPR_LEAF:
	  result = _expr_eval_leaf(expr, levels_deep);
	  break;
      case EXPR_BINARY:
	  result = _expr_eval_binary(expr, levels_deep);
	  break;
      case EXPR_ASSIGNMENT:
	  result = _expr_eval_assignment(expr);
	  break;
      case EXPR_FUNCTIONCALL:
	  result = _expr_eval_functioncall(expr);
	  break;
      default:
	  error_println("expr_eval(): unknown expression type: %d",
			expr->type);
	  return NULL;
      }

    if (result != NULL)
	debug2_printf("%sEvaluates to type: %s, val: %s\n", tabs,
		      leaf_type_to_str(result->u.leaf.type),
		      leaf_val_to_str(result));
    return result;
}

//assumes is EXPR_LEAF
EXPR_P _expr_eval_leaf(EXPR_P expr, int levels_deep)
{
    EXPR_P result = NULL;
    debug2_printf("leaf type=%s, val: %s\n",
		  leaf_type_to_str(expr->u.leaf.type),
		  leaf_val_to_str(expr));

    switch (expr->u.leaf.type)
      {
      case LEAF_INT:
      case LEAF_FLOAT:
      case LEAF_STRING:
      case LEAF_BOOL:
	  result = expr;
	  break;
      case LEAF_LIST:
	  {
	      LLIST_P walker;
	      //NOTE: future versions may have syntax for lists w/ unevale expressions.

	      //if list was defined from list literal (not cons operations) it may have exprs needing to be evaled
	      //e.g. [2,i+1]
	      for (walker = expr->u.leaf.val.list_val; walker != NULL;
		   walker = walker->next)
		{
		    EXPR_P el = walker->element;
		    if (el->type != EXPR_LEAF)	// from a list literal with a func call or binary op expr inside eg [i+1]
		      {
			  //printf("TP1\n");
			  el = walker->element =
			      _expr_eval(el, levels_deep + 1);

		    } else	// is LEAF
		    if (el->u.leaf.type == LEAF_LIST)
		      {
			  // printf("TP2\n");
			  el = walker->element =
			      _expr_eval(el, levels_deep + 1);
		      }

		}
	      result = expr;
	  }
	  break;
      case LEAF_ID:
	  {
	      char *symname = expr->u.leaf.val.id_val;
	      SYMTBL_ENT_P entry;

	      entry = symbol_tbl_get_entry(glob.symbol_tbl_global, symname);	//all func are in global

	      if (entry == NULL)
		  entry = symbol_tbl_get_entry_variable_fatal(symname);


	      if (entry->type == SYM_TYPE_VARIABLE)
		{
		    switch (entry->u.variable.type)
		      {
		      case VAR_TYPE_INT:
			  {
			      int val = entry->u.variable.rvalue.int_val;
			      result = new_expr_leaf_int(val);
			  }
			  break;
		      case VAR_TYPE_FLOAT:
			  {
			      int val = entry->u.variable.rvalue.float_val;
			      result = new_expr_leaf_float(val);
			  }
			  break;
		      case VAR_TYPE_STRING:
			  {
			      char *val =
				  entry->u.variable.rvalue.string_val;
			      result = new_expr_leaf_string(val);
			  }
			  break;
		      case VAR_TYPE_BOOL:
			  {
			      bool val = entry->u.variable.rvalue.bool_val;
			      result = new_expr_leaf_bool(val);
			  }
			  break;
		      case VAR_TYPE_FUNCTION:
			  {
			      char *func_symname =
				  entry->u.variable.rvalue.function_val;
			      result = new_expr_leaf_id(func_symname);
			  }
			  break;
		      case VAR_TYPE_LIST:
			  {
			      LLIST_P val =
				  entry->u.variable.rvalue.list_val;
			      result = new_expr_leaf_list(val);
			  }
			  break;

		      case VAR_TYPE_UNINITIALIZED:
		      default:
			  fatal_error_println("unexpected symbol table var type");	//shouldn't get here?
		      }
	      } else if (entry->type == SYM_TYPE_FUNCTION)
		{
		    result = expr;	//keep it as is
		    break;
		}
	  }
      }
    return result;
}


//assumes is EXPR_BINARY
//NOTE: not EXPR_BINARY does not include assignment opeator (EXPR_ASSIGNMENT is used instead)
EXPR_P _expr_eval_binary(EXPR_P expr, int levels_deep)
{
    BINARY_OP_TYPE op = expr->u.binary_op.op;
    EXPR_P lhs_evaled, rhs_evaled;
    EXPR_P result = NULL;

    debug2_printf("op type=%s, ", binary_op_type_to_str(op));

    lhs_evaled = _expr_eval(expr->lhs, levels_deep + 1);
    if (lhs_evaled == NULL || lhs_evaled->type != EXPR_LEAF)
	return NULL;
    rhs_evaled = _expr_eval(expr->rhs, levels_deep + 1);
    if (rhs_evaled == NULL || rhs_evaled->type != EXPR_LEAF)
	return NULL;

    LEAF_TYPE type_lhs, type_rhs;

    type_lhs = lhs_evaled->u.leaf.type;
    type_rhs = rhs_evaled->u.leaf.type;

    struct st_leaf *leaf_lhs, *leaf_rhs;

    leaf_lhs = &(lhs_evaled->u.leaf);
    leaf_rhs = &(rhs_evaled->u.leaf);

    debug2_printf("LHS type=%s, RHS type=%s\n",
		  leaf_type_to_str(type_lhs), leaf_type_to_str(type_rhs));

    if (type_lhs != type_rhs && op == BINARY_OP_EQUALITY)
      {
	  result = new_expr_leaf_bool(false);	// diff types are never equal
	  return result;
      }
    // else // type_lhs == type_rhs ||  op != BINARY_OP_EQUALITY


    switch (type_lhs)
      {
      case LEAF_INT:
	  {
	      int val_lhs = leaf_lhs->val.int_val;

	      switch (op)
		{
		case BINARY_OP_ADD:
		    if (type_lhs == type_rhs)	/* == LEAF_INT */
			result =
			    new_expr_leaf_int(val_lhs +
					      leaf_rhs->val.int_val);
		    //TODO: make errors for unsupported typemixing here
		    break;
		case BINARY_OP_SUBTRACT:
		    if (type_lhs == type_rhs)	/* == LEAF_INT */
			result =
			    new_expr_leaf_int(val_lhs -
					      leaf_rhs->val.int_val);
		    break;
		case BINARY_OP_MULTIPLY:
		    if (type_lhs == type_rhs)	/* == LEAF_INT */
			result =
			    new_expr_leaf_int(val_lhs *
					      leaf_rhs->val.int_val);
		    break;
		case BINARY_OP_DIVIDE:
		    if (type_lhs == type_rhs)	/* == LEAF_INT */
			result =
			    new_expr_leaf_int(val_lhs /
					      leaf_rhs->val.int_val);
		    break;
		case BINARY_OP_EQUALITY:
		    //NOTE: already checked types are equal for this op case before switch stmt.
		    result =
			new_expr_leaf_bool(val_lhs ==
					   leaf_rhs->val.int_val);
		    break;
		case BINARY_OP_LIST_CONS:
		    result = _expr_eval_binary_op_list_cons(lhs_evaled, rhs_evaled, levels_deep);	//the called func will inc levels_deep
		    break;
		default:
		    fatal_error_println
			("expr_eval(): unknown int op type: %d", op);
		}
	  }
	  break;
      case LEAF_FLOAT:
	  {
	      float val_lhs = leaf_lhs->val.float_val;

	      switch (op)
		{
		case BINARY_OP_ADD:
		    if (type_lhs == type_rhs)	/* == LEAF_FLOAT */
			result =
			    new_expr_leaf_float(val_lhs +
						leaf_rhs->val.float_val);
		    break;
		case BINARY_OP_SUBTRACT:
		    if (type_lhs == type_rhs)	/* == LEAF_FLOAT */
			result =
			    new_expr_leaf_float(val_lhs -
						leaf_rhs->val.float_val);
		    break;
		case BINARY_OP_MULTIPLY:
		    if (type_lhs == type_rhs)	/* == LEAF_FLOAT */
			result =
			    new_expr_leaf_float(val_lhs *
						leaf_rhs->val.float_val);
		    break;
		case BINARY_OP_DIVIDE:
		    if (type_lhs == type_rhs)	/* == LEAF_FLOAT */
			result =
			    new_expr_leaf_float(val_lhs /
						leaf_rhs->val.float_val);
		    break;
		case BINARY_OP_EQUALITY:
		    //NOTE: already checked types are equal for this op case before switch stmt.
		    result =
			new_expr_leaf_bool(val_lhs ==
					   leaf_rhs->val.float_val);
		    break;
		case BINARY_OP_LIST_CONS:
		    result =
			_expr_eval_binary_op_list_cons(lhs_evaled,
						       rhs_evaled,
						       levels_deep);
		    break;

		default:
		    fatal_error_println
			("expr_eval(): unknown float op type: %d", op);
		}
	  }
	  break;
      case LEAF_STRING:
	  {
	      char *val_lhs = leaf_lhs->val.string_val;
	      char *new_str;

	      switch (op)
		{
		case BINARY_OP_ADD:	/* concat */
		    if (type_lhs == type_rhs)	/* == LEAF_STRING */
		      {
			  char *val_rhs = leaf_rhs->val.string_val;
			  new_str = checked_malloc(strlen(val_rhs) + strlen(val_lhs) + 1);	//TODO: need to free
			  strcat(new_str, val_lhs);	/* no need for strncat */
			  strcat(new_str, val_rhs);
			  result = new_expr_leaf_string(new_str);	// does not strdup string
		      }
		    break;
		case BINARY_OP_EQUALITY:
		    //NOTE: already checked types are equal for this op case before switch stmt.
		    {
			char *val_rhs = leaf_rhs->val.string_val;
			result = new_expr_leaf_bool(!strcmp(val_lhs, val_rhs));	//do strcmp, not just pointer  compare!
		    }
		    break;
		case BINARY_OP_LIST_CONS:
		    result =
			_expr_eval_binary_op_list_cons(lhs_evaled,
						       rhs_evaled,
						       levels_deep);
		    break;

		case BINARY_OP_SUBTRACT:
		case BINARY_OP_MULTIPLY:
		case BINARY_OP_DIVIDE:
		default:
		    error_println
			("expr_eval(): unknown string op type: %d", op);
		    return NULL;
		}
	  }
	  break;
      case LEAF_BOOL:
	  {
	      bool val_lhs = leaf_rhs->val.bool_val;

	      switch (op)
		{
		case BINARY_OP_EQUALITY:
		    result =
			new_expr_leaf_bool(val_lhs ==
					   leaf_rhs->val.bool_val);
		    break;
		case BINARY_OP_LIST_CONS:
		    result =
			_expr_eval_binary_op_list_cons(lhs_evaled,
						       rhs_evaled,
						       levels_deep);
		    break;

		case BINARY_OP_ADD:
		    break;
		case BINARY_OP_SUBTRACT:
		    break;
		case BINARY_OP_MULTIPLY:
		    break;
		case BINARY_OP_DIVIDE:
		    break;
		default:
		    fatal_error_println
			("expr_eval(): unknown bool op type: %d", op);
		}
	  }
	  break;

      case LEAF_ID:
	  //TODO:
	  fatal_error_println("NO ID YET!  TODO");	//needed?
	  break;
      case LEAF_LIST:
	  {
	      switch (op)
		{
		case BINARY_OP_EQUALITY:	//already checked if types differ and using OP_EQUALITY
		    {

			LLIST_P lhs_list = leaf_lhs->val.list_val;
			LLIST_P rhs_list = leaf_rhs->val.list_val;

			//check if the lists are diff sizes
			size_t lhs_size = llist_size(lhs_list);
			size_t rhs_size = llist_size(rhs_list);
			if (lhs_size != rhs_size)
			    return new_expr_leaf_bool(false);
			//else same size

			LLIST_P lhs_walker = lhs_list;
			LLIST_P rhs_walker = rhs_list;
			while (lhs_walker != NULL && rhs_walker != NULL)
			  {
			      EXPR_P el_l = lhs_walker->element;
			      EXPR_P el_r = rhs_walker->element;

			      if (el_l->type != el_r->type)	//type of element doesn't match
				  return new_expr_leaf_bool(false);

			      //TODO: finish doing deep comparison.

			      lhs_walker = lhs_walker->next;
			      rhs_walker = rhs_walker->next;
			  }

			return new_expr_leaf_bool(true);
		    }
		    break;
		case BINARY_OP_LIST_CONS:
		    result =
			_expr_eval_binary_op_list_cons(lhs_evaled,
						       rhs_evaled,
						       levels_deep);
		    break;
		case BINARY_OP_ADD:
		    break;
		case BINARY_OP_SUBTRACT:
		    break;
		case BINARY_OP_MULTIPLY:
		    break;
		case BINARY_OP_DIVIDE:
		    break;
		default:
		    fatal_error_println
			("expr_eval(): unknown list op type: %d", op);
		}

	  }
	  break;
      default:
	  fatal_error_println("expr_eval_binary(): unknown lhs type: %d",
			      type_lhs);
      }

    return result;
}

//NOTE: expects leaf_list_rhs->u.leaf.val.list_val LLIST_P to be an llist of EXPR_P
//NOTE: expects LHS to be evaled already.
EXPR_P _expr_eval_binary_op_list_cons(EXPR_P lhs_evaled,
				      EXPR_P leaf_list_rhs,
				      int levels_deep)
{
    EXPR_P result = NULL;
    LLIST_P new_list = NULL;
    LLIST_P rhs_list, walker;

    debug2_printf("cons: lhs %s , rhs type %s\n",
		  expr_type_to_str(lhs_evaled->type),
		  expr_type_to_str(leaf_list_rhs->type));


    if (lhs_evaled == NULL || lhs_evaled->type != EXPR_LEAF)
	fatal_error_println("lhs_evaled is not a leaf expr\n");	//shouldn't ever happen



    if (leaf_list_rhs->type != EXPR_LEAF)
	fatal_error_println("cons operator must have rhs type LIST. rhs was not leaf");	//a process-time error
    if (leaf_list_rhs->u.leaf.type != LEAF_LIST)	//::[] or ::list
	fatal_error_println
	    ("cons operator must have rhs type LIST. rhs is %s",
	     leaf_type_to_str(leaf_list_rhs->u.leaf.type));

    rhs_list = leaf_list_rhs->u.leaf.val.list_val;

    debug2_printf("cons: lhs type=%s , rhs type=LIST\n",
		  leaf_type_to_str(lhs_evaled->u.leaf.type));

    llist_append(lhs_evaled, &new_list);	//append to empty list is equivalent to prepend. always anempty new_list.


    for (walker = rhs_list; walker != NULL; walker = walker->next)
      {
	  EXPR_P el = walker->element;
	  if (el->type != EXPR_LEAF)	// from a list literal with a func call or binary op expr inside eg [i+1]
	      el = _expr_eval(el, levels_deep + 1);

	  llist_append(el, &new_list);
      }

    result = new_expr_leaf_list(new_list);
    return result;
}


//assumes is EXPR_ASSIGNMENT
    //returns rhs's evaluation (always a leaf expr)
EXPR_P _expr_eval_assignment(EXPR_P expr)
{
    EXPR_P lhs = expr->lhs;
    EXPR_P rhs = expr->rhs;

    //rhs
    if (rhs == NULL)
	fatal_error_println("assignment rhs NULL");	//shouldn't ever get here

    EXPR_P rhs_result = expr_eval(rhs);
    //what will be returned: rhs_result

    if (rhs_result == NULL)
	fatal_error_println("assignment rhs_result NULL");	//shouldn't ever get here

    LEAF_TYPE rhs_res_type = rhs_result->u.leaf.type;

//printf("rhsresult EXPRtype=%s,leaftype=%s\n", expr_type_to_str(rhs_result->type), leaf_type_to_str(rhs_res_type));

//handle lhs
    if (lhs == NULL)
	fatal_error_println("assignment lhs NULL");	//shouldn't ever get here

    if (lhs->type != EXPR_LEAF)
	fatal_error_println("Unexpected assignment lhs  not type EXPR_LEAF");	//shouldn't ever get here

    if (lhs->u.leaf.type != LEAF_ID)
	fatal_error_println("Unexpected assignment lhs leaf is not type LEAF_ID");	//shouldn't ever get here

    char *lhs_symname;

    lhs_symname = lhs->u.leaf.val.id_val;	//id_val is a symname
    SYMTBL_ENT_P entry;
    entry = symbol_tbl_get_entry_variable_fatal(lhs_symname);	//get lhs of assignment from (local) symboltbl

    switch (entry->u.variable.type)
      {
      case VAR_TYPE_INT:
	  if (rhs_res_type != LEAF_INT)
	      fatal_error_println
		  ("Type Mismatch assignment lhs %s INT, rhs %s",
		   lhs_symname, leaf_type_to_str(rhs_res_type));

	  entry->u.variable.rvalue.int_val =
	      rhs_result->u.leaf.val.int_val;
	  break;
      case VAR_TYPE_FLOAT:
	  if (rhs_res_type != LEAF_FLOAT)
	      fatal_error_println
		  ("Type Mismatch assignment lhs %s FLOAT, rhs %s",
		   lhs_symname, leaf_type_to_str(rhs_res_type));

	  entry->u.variable.rvalue.float_val =
	      rhs_result->u.leaf.val.float_val;
	  break;
      case VAR_TYPE_STRING:
	  if (rhs_res_type != LEAF_STRING)
	      fatal_error_println
		  ("Type Mismatch assignment lhs %s STRING, rhs %s",
		   lhs_symname, leaf_type_to_str(rhs_res_type));

	  entry->u.variable.rvalue.string_val =
	      rhs_result->u.leaf.val.string_val;
	  break;
      case VAR_TYPE_BOOL:
	  if (rhs_res_type != LEAF_BOOL)
	      fatal_error_println
		  ("Type Mismatch assignment lhs %s BOOL, rhs %s",
		   lhs_symname, leaf_type_to_str(rhs_res_type));

	  entry->u.variable.rvalue.bool_val =
	      rhs_result->u.leaf.val.bool_val;
	  break;
      case VAR_TYPE_FUNCTION:
	  if (rhs_res_type != LEAF_ID)	//TODO?? MAYBE
	      fatal_error_println
		  ("Type Mismatch assignment lhs %s FUNCTION, rhs %s",
		   lhs_symname, leaf_type_to_str(rhs_res_type));

	  entry->u.variable.rvalue.string_val =
	      rhs_result->u.leaf.val.id_val;
	  break;
      case VAR_TYPE_LIST:
	  if (rhs_res_type != LEAF_LIST)
	      fatal_error_println
		  ("Type Mismatch assignment lhs %s LIST, rhs %s",
		   lhs_symname, leaf_type_to_str(rhs_res_type));

	  entry->u.variable.rvalue.list_val =
	      rhs_result->u.leaf.val.list_val;
	  break;
      case VAR_TYPE_UNINITIALIZED:
	  /* var s; s = 3; # 2nd stmt (expr stmt wth assignment expr) has lhs UNINITIALIZED) */
	  switch (rhs_res_type)
	    {
	    case LEAF_INT:
		entry->u.variable.type = VAR_TYPE_INT;	//infer type from rhs
		entry->u.variable.rvalue.int_val =
		    rhs_result->u.leaf.val.int_val;
		break;
	    case LEAF_FLOAT:
		entry->u.variable.type = VAR_TYPE_FLOAT;
		entry->u.variable.rvalue.float_val =
		    rhs_result->u.leaf.val.float_val;
		break;
	    case LEAF_STRING:
		entry->u.variable.type = VAR_TYPE_STRING;
		entry->u.variable.rvalue.string_val =
		    rhs_result->u.leaf.val.string_val;
		break;
	    case LEAF_BOOL:
		entry->u.variable.type = VAR_TYPE_BOOL;
		entry->u.variable.rvalue.bool_val =
		    rhs_result->u.leaf.val.bool_val;
		break;
	    case LEAF_ID:
		{
		    char *rhs_symname = rhs_result->u.leaf.val.id_val;
		    SYMTBL_ENT_P func_entry =
			symbol_tbl_get_entry(glob.symbol_tbl_global,
					     rhs_symname);
		    if (func_entry == NULL)	//all globals are functions
			fatal_error_println("\nAssignment Expr: lhs variable %s , rhs id %s is not a function.", lhs_symname, rhs_symname);	//will happen from programmer error.
		    entry->u.variable.type = VAR_TYPE_FUNCTION;
		    entry->u.variable.rvalue.function_val = rhs_symname;


		}
		break;
	    case LEAF_LIST:
		entry->u.variable.type = VAR_TYPE_LIST;
		entry->u.variable.rvalue.list_val =
		    rhs_result->u.leaf.val.list_val;
		break;

	    default:
		fatal_error_println("\nAssignment Expr: lhs variable %s , rhs type not int,float,string,bool,function,list", lhs_symname);	//shouldn't get here
	    }			// end switch(rhs_evaled_type)
	  break;
      default:
	  fatal_error_println("\nAssignment Expr: lhs variable %s : unknown type.", lhs_symname);	//shouldn't get here.
      }				//end switch var type
    return rhs_result;		/* value of assignment exp is rhs_result */
}

//assumes is EXPR_FUNCTIONCALL
//NOTE: used in calling main() under the table.
EXPR_P _expr_eval_functioncall(EXPR_P expr)
{
    EXPR_P ret = NULL;
    char *funcname, *actual_funcname;
    SYMTBL_ENT_P entry;
    LLIST_P asl;
    LLIST_P args_expr_list;	//llist of EXPR_P
    LLIST_P walker;
    EXPR_P args_expr_evaled[9];	//$1 is at index=0, $9 is at index=8
    char param[3] = { '$', '0', '\0' };

    args_expr_list = expr->u.functioncall.args_expr_list;

//funcnme is lexical id from src code
    funcname = expr->u.functioncall.funcname;	//NOTE: funcname here may be a variable (TyPE_FUNCTION) also. funcname is a misnomer!
    EXPR_P expr_id = new_expr_leaf_id(funcname);
    EXPR_P actual_funcname_expr = expr_eval(expr_id);	//evaluate it (it might be a function variable).

    if (actual_funcname_expr->type != EXPR_LEAF
	&& actual_funcname_expr->u.leaf.type != LEAF_ID)
	fatal_error_println
	    ("expr_eval_functioncall: Was expecting leaf id");
    //unknown if it gets here.TODO chk

    actual_funcname = actual_funcname_expr->u.leaf.val.id_val;


    entry = symbol_tbl_get_entry(glob.symbol_tbl_global, actual_funcname);
    if (entry == NULL)
      {
	  if (!strcmp(funcname, "main"))	// empty program is OK. it never had 'main' on symbol tbl. 
	      return NULL;

	  fatal_error_println
	      ("expr_eval_functioncall: Reference to undeclared symbol: %s",
	       actual_funcname);
      }
//NOTE: !SYM_TYPE_FUNCTION => SYM_TYPE_VARIABLE
    if (entry->type != SYM_TYPE_FUNCTION
	&& entry->u.variable.type != VAR_TYPE_FUNCTION)
	fatal_error_println
	    ("id %s is not a function or function typed variable.",
	     actual_funcname);


    if (entry->u.function.asl != NULL)	/* user-defined function */
      {
	  /*$1 - $9; evaluated from left to right */
	  int i = 1;
	  for (walker = args_expr_list;
	       walker != NULL && i <= 9; walker = walker->next, i++)
	    {
		EXPR_P tmp = walker->element;
		args_expr_evaled[i - 1] = expr_eval(tmp);
		if ((args_expr_evaled[i - 1])->type != EXPR_LEAF)	//shoulnt happen.
		    fatal_error_println("Expected EXPR_LEAF... symbol: %s",
					funcname);
	    }

//expr's were evaluated with old top of stack symtbl.
/* create new local symtbl */
//TODO: free it at func return
	  SYMTBL_P symbol_tbl_local = symbol_tbl_create();
	  symbol_tbl_local_stack_push(symbol_tbl_local);

/* create positional params */
	  /* $0 is function name */
	  symbol_tbl_new_entry_variable_from_leafexpr(symbol_tbl_local,
						      param /* "$0" */ ,
						      "" /* no file */ ,
						      actual_funcname_expr);

	  for (i = 0, walker = args_expr_list; walker != NULL && i < 9;
	       walker = walker->next, i++)
	    {
		(param[1])++;	//increment ASCII 0 to 1...9
//TODO: maybe remove existing?
//this func asserts it is EXPR_LEAF type expr
		symbol_tbl_new_entry_variable_from_leafexpr
		    (symbol_tbl_local, param /* "$_" */ ,
		     "" /* no file */ ,
		     args_expr_evaled[i]);	// [0] is $1, [8] is $9
		//TODO: err chk
	    }



/* call the function */

	  asl = entry->u.function.asl;
	  debug2_printf("Processing ASL[%s]\n", funcname);
	  asl_process_asl(asl);

	  symbol_tbl_local = symbol_tbl_local_stack_pop();	//returns same value as symbol_tbl_local already was
	  hashtbl_destroy(symbol_tbl_local);

	  ret = glob.current_function_return;	//might be NULL
	  glob.current_function_return = NULL;
    } else if (entry->u.function.builtin_funcp != NULL)	/* built-in function */
      {
	  int argc = 0;
	  for (walker = args_expr_list; walker != NULL;	//count them
	       walker = walker->next)
	      argc++;

	  if (argc != 1)
	      fatal_error_println
		  ("builtin func must be used with 1 arg [argc=%d].\n",
		   argc);

	  EXPR_P tmp = args_expr_list->element;	//get first element and eval
	  EXPR_P tmp_evaled = expr_eval(tmp);

	  ret = entry->u.function.builtin_funcp(tmp_evaled);
    } else
	fatal_error_println("asl NULL,builtin funcp NULL\n");	//shouldnt get here




    return ret;			//already eval'ed. or else is NULL if no return stmt.

}
